package ru.aslanov.schedule.server;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import ru.aslanov.schedule.model.Group;
import ru.aslanov.schedule.model.Schedule;
import ru.aslanov.schedule.utils.JSUtil;
import ru.aslanov.schedule.utils.XMLUtil;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import javax.jdo.annotations.PersistenceAware;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * Created: Feb 1, 2010 1:30:23 PM
 *
 * @author Sergey Aslanov
 */
@PersistenceAware
public class UploadScheduleServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(UploadScheduleServlet.class.getName());

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        try {
            AccessManager.getInstance().checkAdmin();
        } catch (AccessDeniedException e) {
            throw new ServletException(e);
        }

        String callbackName = request.getParameter("callback");

        final PersistenceManager pm = PMF.getThreadLocalPersistenceManager();
        try {

            // Create a factory for disk-based file items
            FileItemFactory factory = new MyFileItemFactory();
            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
            // Parse the request
            List /* FileItem */ items = upload.parseRequest(request);
            String key = null;
            for (Object item : items) {
                FileItem fileItem = (FileItem) item;
                if (!fileItem.isFormField()) {
                    final InputStream inputStream = fileItem.getInputStream();
                    try {
                        final String fileName = fileItem.getName();
                        String scheduleName = fileName + " " + new Date();
                        key = storeSchedule(pm, inputStream, scheduleName);
                    } finally {
                        inputStream.close();
                    }
                }
            }

            printResult(response.getWriter(), true, null, key, callbackName);

        } catch (Exception e) {
            log.log(java.util.logging.Level.WARNING, "Upload error", e);
            printResult(response.getWriter(), false, e.toString(), null, callbackName);
        } //finally {
        //    pm.close();
        //}
    }

    private String storeSchedule(PersistenceManager pm, InputStream inputStream, String scheduleName) throws JAXBException {
        final Unmarshaller unmarshaller = XMLUtil.JAXB_CONTEXT.createUnmarshaller();
        Schedule schedule = (Schedule) unmarshaller.unmarshal(inputStream);

        //getStats(persistenceManager);
        final Transaction transaction = pm.currentTransaction();
        try {

            Schedule scheduleDb = new Schedule();
            scheduleDb.setName(scheduleName);
            scheduleDb.setDances(schedule.getDances());
            scheduleDb.setLevels(schedule.getLevels());
            scheduleDb.setTeachers(schedule.getTeachers());
            scheduleDb.setLocations(schedule.getLocations());
            scheduleDb.setCalendars(schedule.getCalendars());

            transaction.begin();

            pm.makePersistent(scheduleDb);

            scheduleDb.setGroups(schedule.getGroups());
            for (Group group : scheduleDb.getGroups()) {
                group.syncDays(null);
            }

            transaction.commit();

            return scheduleDb.getEncodedKey();
        } finally {
            if (transaction.isActive()) transaction.rollback();
        }
    }

    private static void printResult(PrintWriter writer, boolean success, String description, String scheduleKey, String callbackName) {
        JSUtil.printResponse(writer, callbackName, false, false,
                new JSUtil.Param("success", success),
                new JSUtil.Param("dsc", description),
                new JSUtil.Param("scheduleKey", scheduleKey));
    }

    private static class MyFileItemFactory implements FileItemFactory {
        @Override
        public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) {
            return new MyFileItem(fieldName, contentType, isFormField, fileName);
        }

        private static class MyFileItem implements FileItem {
            private String fieldName;
            private String contentType;
            private boolean isFormField;
            private String fileName;
            private ByteArrayOutputStream baos;

            public MyFileItem(String fieldName, String contentType, boolean formField, String fileName) {
                this.fieldName = fieldName;
                this.contentType = contentType;
                this.isFormField = formField;
                this.fileName = fileName;
                baos = new ByteArrayOutputStream();
            }

            @Override
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(get());
            }

            @Override
            public String getContentType() {
                return contentType;
            }

            @Override
            public String getName() {
                return fileName;
            }

            @Override
            public boolean isInMemory() {
                return true;
            }

            @Override
            public long getSize() {
                return baos.size();
            }

            @Override
            public byte[] get() {
                return baos.toByteArray();
            }

            @Override
            public String getString(String encoding) throws UnsupportedEncodingException {
                return new String(get(), encoding);
            }

            @Override
            public String getString() {
                return new String(get());
            }

            @Override
            public void write(File file) throws Exception {
                throw new UnsupportedOperationException();
            }

            @Override
            public void delete() {
                baos = null;
            }

            @Override
            public String getFieldName() {
                return fieldName;
            }

            @Override
            public void setFieldName(String name) {
                this.fieldName = name;
            }

            @Override
            public boolean isFormField() {
                return isFormField;
            }

            @Override
            public void setFormField(boolean state) {
                isFormField = state;
            }

            @Override
            public OutputStream getOutputStream() throws IOException {
                return baos;
            }
        }
    }
}
